use super::config_manager::ConfigManager;

use base64::{engine::general_purpose, Engine as _};
use serde::Serialize;
use std::fs;
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use tauri::State;

#[tauri::command]
pub async fn open_source_homepage() -> Result<(), ()> {
    let _ = webbrowser::open("https://openi.pcl.ac.cn/huolongshe/spider-map-tauri");

    Ok(())
}

#[tauri::command]
pub async fn read_text_file(file_path: String) -> Result<Option<String>, ()> {
    let content_result = tokio::fs::read_to_string(file_path).await;

    if let Ok(content) = content_result {
        Ok(Some(content))
    } else {
        Ok(None)
    }
}

#[derive(Serialize)]
pub struct TractFile {
    file_name: String,
    file_content: String,
}

#[tauri::command]
pub async fn load_track_files(file_paths: Vec<String>) -> Result<Vec<TractFile>, ()> {
    let result: Vec<TractFile> = file_paths
        .into_iter()
        .map(move |file_path| {
            let file_content = fs::read_to_string(&file_path).unwrap_or(String::from(""));
            TractFile {
                file_name: Path::new(&file_path)
                    .file_name()
                    .unwrap()
                    .to_string_lossy()
                    .to_string(),
                file_content,
            }
        })
        .collect();

    Ok(result)
}

#[tauri::command]
pub async fn load_map_config(
    config_manager: State<'_, ConfigManager>,
) -> Result<Option<String>, ()> {
    let file_path = &config_manager.map_config_path;

    let map_config = tokio::fs::read_to_string(file_path).await;

    if let Ok(config) = map_config {
        Ok(Some(config))
    } else {
        Ok(None)
    }
}

#[tauri::command]
pub async fn load_user_data(
    config_manager: State<'_, ConfigManager>,
) -> Result<Option<String>, ()> {
    let user_data = tokio::fs::read_to_string(&config_manager.user_data_path).await;
    if let Ok(data) = user_data {
        Ok(Some(data))
    } else {
        Ok(None)
    }
}

#[tauri::command]
pub async fn load_track_data(
    track_uuid: String,
    config_manager: State<'_, ConfigManager>,
) -> Result<String, ()> {
    let track_path = config_manager
        .track_data_path
        .join(PathBuf::from(format!("{track_uuid}.tps")));

    let track_data = tokio::fs::read_to_string(&track_path).await;

    if let Ok(data) = track_data {
        Ok(data)
    } else {
        Ok(String::from("[]"))
    }
}

#[tauri::command]
pub async fn load_visible_tracks(config_manager: State<'_, ConfigManager>) -> Result<String, ()> {
    let visible_tracks = tokio::fs::read_to_string(&config_manager.visible_track_path).await;
    if let Ok(data) = visible_tracks {
        Ok(data)
    } else {
        Ok(String::from("[]"))
    }
}

#[tauri::command]
pub async fn delete_track_data(
    track_uuid: String,
    config_manager: State<'_, ConfigManager>,
) -> Result<(), ()> {
    let track_path = config_manager
        .track_data_path
        .join(PathBuf::from(format!("{track_uuid}.tps")));
    let _ = tokio::fs::remove_file(&track_path).await;
    Ok(())
}

#[tauri::command]
pub async fn save_current_path(
    current_path: String,
    config_manager: State<'_, ConfigManager>,
) -> Result<(), ()> {
    let _ = tokio::fs::write(&config_manager.current_path_cfg, current_path).await;
    Ok(())
}

#[tauri::command]
pub async fn save_user_data(
    data: String,
    config_manager: State<'_, ConfigManager>,
) -> Result<(), ()> {
    let _ = tokio::fs::write(&config_manager.user_data_path, &data).await;
    Ok(())
}

#[tauri::command]
pub async fn save_track_data(
    track_uuid: String,
    track_data: String,
    config_manager: State<'_, ConfigManager>,
) -> Result<(), ()> {
    let track_path = config_manager
        .track_data_path
        .join(PathBuf::from(format!("{track_uuid}.tps")));
    let _ = tokio::fs::write(&track_path, &track_data).await;
    Ok(())
}

#[tauri::command]
pub fn save_snapshot_and_exit(
    user_data: String,
    map_config: String,
    visible_track_uuids: String,
    config_manager: State<'_, ConfigManager>,
) {
    let _ = fs::write(&config_manager.user_data_path, user_data);
    let _ = fs::write(&config_manager.map_config_path, map_config);
    let _ = fs::write(&config_manager.visible_track_path, visible_track_uuids);
    std::process::exit(0);
}

#[tauri::command]
pub async fn save_track_file(file_path: String, file_content: String) -> Result<(), ()> {
    let _ = tokio::fs::write(&file_path, &file_content).await;
    Ok(())
}

#[tauri::command]
pub async fn save_map_screenshot(file_path: String, img_url: String) -> Result<(), ()> {
    // 1. 分离 data URL 的元数据和 base64 数据部分
    let parts: Vec<&str> = img_url.split(',').collect();
    if parts.len() != 2 {
        return Ok(());
    }

    // 2. 检查数据格式并提取 base64 部分
    let base64_data = if parts[0].contains("base64") {
        parts[1]
    } else {
        return Ok(());
    };

    // 3. 解码 base64 数据
    let decoded_data = general_purpose::STANDARD
        .decode(base64_data)
        .unwrap_or(Vec::new());

    // 4. 写入文件
    let _ = fs::write(&file_path, &decoded_data);

    Ok(())
}

#[derive(Serialize)]
pub struct LonLat {
    lon: String,
    lat: String,
}

#[tauri::command]
pub async fn get_tianditu_route(url: String) -> Result<Vec<LonLat>, ()> {
    let response = reqwest::get(&url).await;
    if response.is_err() {
        return Err(());
    }

    let text = response.unwrap().text().await;
    if text.is_err() {
        return Err(());
    }

    let text = text.unwrap();

    let mut route: Vec<LonLat> = Vec::new();
    let mut start = 0;

    while let Some(open_start) = text[start..].find("<streetLatLon>") {
        let open_pos = start + open_start + 14; // +14 for "<streetLatLon>"

        if let Some(close_start) = text[open_pos..].find("</streetLatLon>") {
            let close_pos = open_pos + close_start;
            let points_str = &text[open_pos..(close_pos - 1)]; // 最后一个坐标后面有一个分号（;）,所以需要减一
            let points: Vec<&str> = points_str.split(';').collect();

            for point in points {
                let point_vec: Vec<&str> = point.split(',').collect();
                let lon_lat = LonLat {
                    lon: String::from(point_vec[0]),
                    lat: String::from(point_vec[1]),
                };
                route.push(lon_lat);
            }

            start = close_pos + 15; // +15 for "</streetLatLon>"
        } else {
            break;
        }
    }

    Ok(route)
}

#[tauri::command]
pub async fn http_get(url: String) -> Result<String, ()> {
    let response = reqwest::get(&url).await;
    if response.is_err() {
        return Err(());
    }

    let text = response.unwrap().text().await;
    if text.is_err() {
        return Err(());
    }

    Ok(text.unwrap())
}

#[derive(Serialize)]
pub struct Photo {
    error: String,
    label: String,
    url: String,
    lon: f64,
    lat: f64,
    alt: f64,
    timestamp: String,
    direction: f64,
}

#[tauri::command]
pub async fn open_photo_file(file_path: String) -> Result<Photo, ()> {
    let format = if file_path.ends_with(".png") {
        "png"
    } else {
        "jpeg"
    };

    let file_name = Path::new(&file_path)
        .file_name()
        .unwrap()
        .to_string_lossy()
        .to_string();

    let mut result = Photo {
        error: String::new(),
        label: file_name,
        url: String::new(),
        lon: 0.0,
        lat: 0.0,
        alt: 0.0,
        timestamp: String::new(),
        direction: 0.0,
    };

    let file = File::open(&file_path);

    if file.is_err() {
        result.error.push_str("打开文件出错！");
        return Ok(result);
    }

    let mut bufreader = BufReader::new(file.unwrap());
    let exif_reader = exif::Reader::new();
    let exif_info = exif_reader.read_from_container(&mut bufreader);

    if exif_info.is_err() {
        result.error.push_str("读取文件Exif信息出错！");
        return Ok(result);
    }

    let exif_info = exif_info.unwrap();

    if let Some(lon) = parse_gps_coordinate(
        &exif_info,
        exif::Tag::GPSLongitude,
        exif::Tag::GPSLongitudeRef,
    ) {
        result.lon = lon;
    } else {
        result.error.push_str("该照片无经纬度坐标信息！");
        return Ok(result);
    }

    if let Some(lat) = parse_gps_coordinate(
        &exif_info,
        exif::Tag::GPSLatitude,
        exif::Tag::GPSLatitudeRef,
    ) {
        result.lat = lat;
    } else {
        result.error.push_str("该照片无经纬度坐标信息！");
        return Ok(result);
    }

    if let Some(field) = exif_info.get_field(exif::Tag::GPSAltitude, exif::In::PRIMARY) {
        result.alt = field.display_value().to_string().parse().unwrap_or(0.0);
    }

    if let Some(field) = exif_info.get_field(exif::Tag::GPSImgDirection, exif::In::PRIMARY) {
        result.direction = field.display_value().to_string().parse().unwrap_or(0.0);
    }

    if let Some(field) = exif_info.get_field(exif::Tag::DateTime, exif::In::PRIMARY) {
        let timestamp = &field.display_value().to_string();
        result
            .timestamp
            .push_str(&(timestamp.replace(" ", "T") + "Z"));
    }

    let path_buf = PathBuf::from(file_path);
    let buffer = fs::read(path_buf);

    if buffer.is_err() {
        result.error.push_str("打开文件出错！");
        return Ok(result);
    }

    let base64_data = general_purpose::STANDARD.encode(buffer.unwrap());
    let data_url = format!("data:image/{};base64,{}", format, base64_data);
    result.url.push_str(&data_url);

    Ok(result)
}

/// 辅助函数：从 EXIF 数据中提取并格式化纬度或经度
fn parse_gps_coordinate(
    exif_data: &exif::Exif,
    coord_tag: exif::Tag,
    ref_tag: exif::Tag,
) -> Option<f64> {
    let coord = exif_data.get_field(coord_tag, exif::In::PRIMARY)?;
    let ref_val = exif_data.get_field(ref_tag, exif::In::PRIMARY)?;

    if let exif::Value::Rational(coords) = &coord.value {
        if coords.len() == 3 {
            let degrees = coords[0].to_f64();
            let minutes = coords[1].to_f64();
            let seconds = coords[2].to_f64();

            let decimal = degrees + minutes / 60.0 + seconds / 3600.0;

            // 根据参考方向调整正负
            let ref_str = ref_val.display_value().to_string();
            if ref_str == "S" || ref_str == "W" {
                return Some(-decimal);
            } else {
                return Some(decimal);
            }
        }
    }
    None
}
